
我们已经把累加和求和搞定了，可以想象其他种类的积累，可以将给定值序列相乘；或者，将字符串连接起来。即使在序列中找到最大值，也可以表述为一个积累问题。所有这些替代方案中，唯一需要更改的accum()操作是total += *beg。这个操作可以称为积累策略。

下面是如何在accum()函数模板中引入策略的示例:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{traits/accum6.hpp}
\begin{lstlisting}[style=styleCXX]
#ifndef ACCUM_HPP
#define ACCUM_HPP

#include "accumtraits4.hpp"
#include "sumpolicy1.hpp"

template<typename T,
		typename Policy = SumPolicy,
		typename Traits = AccumulationTraits<T>>
auto accum (T const* beg, T const* end)
{
	using AccT = typename Traits::AccT;
	AccT total = Traits::zero();
	while (beg != end) {
		Policy::accumulate(total, *beg);
		++beg;
	}
	return total;
}

#endif // ACCUM_HPP
\end{lstlisting}

accum()的这个版本中，SumPolicy是一个策略类，也就是说，这个类通过一个接口为一个算法实现一个或多个策略。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}可以将其推广为策略参数，可以是类(如前所述)或指向函数的指针。
\end{tcolorbox}

SumPolicy可以这样写:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{traits/sumpolicy1.hpp}
\begin{lstlisting}[style=styleCXX]
#ifndef SUMPOLICY_HPP
#define SUMPOLICY_HPP
class SumPolicy {
	public:
	template<typename T1, typename T2>
	static void accumulate (T1& total, T2 const& value) {
		total += value;
	}
};
#endif // SUMPOLICY_HPP
\end{lstlisting}

通过指定不同的策略来积累值，可以计算不同的东西。下面的程序，打算确定一些值的乘积:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{traits/accum6.cpp}
\begin{lstlisting}[style=styleCXX]
#include "accum6.hpp"
#include <iostream>

class MultPolicy {
	public:
	template<typename T1, typename T2>
	static void accumulate (T1& total, T2 const& value) {
		total *= value;
	}
};
int main()
{
	// create array of 5 integer values
	int num[] = { 1, 2, 3, 4, 5 };
	
	// print product of all values
	std::cout << "the product of the integer values is "
			<< accum<int,MultPolicy>(num, num+5)
			<< ’\n’;
}
\end{lstlisting}

然而，这个程序的输出并不是我们想要的:

\begin{tcblisting}{commandshell={}}
the product of the integer values is 0
\end{tcblisting}

这里的问题是由初值的选择引起的:尽管0适用于求和，但它不适用于乘法(零初值会导致累积乘法的结果为零)。这说明了不同的特征和政策可能会相互影响，强调了设计模板的重要性。

可以了解到累积循环的初始化，也是累积策略的一部分。这个策略可能使用特征zero()，也可能不使用。其他替代方案也不能忽略:不是所有问题都必须用特性和策略来解决。例如，C++标准库的std::accumulate()函数将初始值作为第三个(函数调用)参数。

\subsubsubsection{19.2.1\hspace{0.2cm}特征和策略:有什么不同?}

合理的例子可以支持这样一个事实，即策略只是特征的特殊情况。相反，也可以说，特征就是策略的一种编码。

《新简明牛津英语词典》(参见[NewShorterOED])是这样说的:

\begin{itemize}
\item 
trait n.…特征:表征一事物的显著特征

\item 
policy n. …作为有利或权宜之计而采取的行动方针
\end{itemize}

基于此，我们倾向于将术语策略类的使用限制为对某种类型的操作进行编码的类，这些操作与其他模板参数基本上是无关的。这与Andrei Alexandrescu在他的《现代C++设计》一书中的观点一致(见[AlexandrescuDesign]第8页):

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}Alexandrescu一直是策略界的主要声音，他在此基础上开发了一套丰富的技术。
\end{tcolorbox}

\textit{政策与特征有很多共同点，但不同之处在于它们不太强调类型，而更多地强调行为。}

引入特征技术的Nathan Myers提出了更开放的定义(见[MyersTraits]):

\textit{特征类:用来代替模板参数的类。作为一个类，聚合有用的类型和常量;作为模板，为解决所有“间接级”软件问题的提供了一条途径。}

因此，我们倾向于使用以下(稍微模糊的)定义:

\begin{itemize}
\item 
特征表示模板参数的自然属性。

\item 
策略表示泛型函数和类型的可配置行为(通常带有一些常用的默认值)。
\end{itemize}

为了进一步阐述这两个概念之间的区别，我们列出了以下关于特征的观察结果:

\begin{itemize}
\item 
特性可以固化使用(例如，不需要通过模板参数传递)。

\item 
特性参数通常有非常自然的默认值(很少重写，或者不能重写)。

\item 
特性参数往往紧密地依赖于一个或多个主要参数。

\item 
特征主要组合类型和常量，而不是成员函数。

\item 
特征倾向于在特征模板中进行收集。
\end{itemize}

对于策略类，有以下观察:

\begin{itemize}
\item 
若策略类没有作为模板参数传递，那其作用不大。

\item 
策略参数不需要有默认值，通常显式指定(尽管许多通用组件都配置了常用的默认策略)。

\item 
策略参数大多与模板的其他参数无关。

\item 
策略类通常组合成员函数。

\item 
策略可以在普通类或类模板中进行收集。
\end{itemize}

然而，这两个术语之间肯定有一条模糊的界线。例如，C++标准库的字符特征也定义了函数行为，如比较、移动和查找字符。通过替换这些特征，可以定义不区分大小写的字符串类(参见[JosuttisStdLib]中的13.2.15节)，同时保持相同的字符类型。尽管称为特征，也有一些与策略相关的属性。

\subsubsubsection{19.2.2\hspace{0.2cm}成员模板与双重模板参数}

为了实现累积策略，选择将SumPolicy和multipolicy表示为具有成员模板的普通类。另一种方法是使用类模板设计策略类接口，然后将类模板用作模板参数(参见第5.7节和第12.2.3节)。可以将SumPolicy重写为模板:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{traits/sumpolicy2.hpp}
\begin{lstlisting}[style=styleCXX]
#ifndef SUMPOLICY_HPP
#define SUMPOLICY_HPP

template<typename T1, typename T2>
class SumPolicy {
	public:
	static void accumulate (T1& total, T2 const& value) {
		total += value;
	}
};

#endif // SUMPOLICY_HPP
\end{lstlisting}

Accum的接口可以适应使用双重模板参数:

\hspace*{\fill} \\ %插入空行
\noindent
\textit{traits/accum7.hpp}
\begin{lstlisting}[style=styleCXX]
#ifndef ACCUM_HPP
#define ACCUM_HPP

#include "accumtraits4.hpp"
#include "sumpolicy2.hpp"

template<typename T,
		template<typename,typename> class Policy = SumPolicy,
		typename Traits = AccumulationTraits<T>>
auto accum (T const* beg, T const* end)
{
	using AccT = typename Traits::AccT;
	AccT total = Traits::zero();
	while (beg != end) {
		Policy<AccT,T>::accumulate(total, *beg);
		++beg;
	}
	return total;
}

#endif // ACCUM_HPP
\end{lstlisting}

同样的变换也可以应用于特征参数(其他变体也有可能:与其显式地将AccT类型传递给策略类型，不如传递积累特征，并让策略从特征参数确定其结果的类型)。

通过双重模板参数访问策略类的主要优点是，使策略类更容易携带一些依赖于模板参数的类型的状态信息(即静态数据成员)。(第一种方法中，静态数据成员必须嵌入到成员类模板中。)

然而，双重模板参数方法的缺点是，现在必须将策略类改写为模板，并使用接口定义的一组精确的模板参数。这会使特征本身的表达式比简单的非模板类更冗长、更不自然。

\subsubsubsection{19.2.3\hspace{0.2cm}组合多种策略和/或特征}

特性和策略并没有完全消除多个模板参数，但确实将数量减少到可控的程度。那么，如何排列这些参数？

一个简单的策略，根据参数默认值选择的可能性的增加对参数进行排序。通常，特征参数遵循策略参数，因为后者在外部代码中更经常重写。(细心的读者可能在演示过程中注意到了这个策略)

如果增加大量的复杂性代码，那么存在一种替代方法，可以任意顺序指定非默认参数。有关详细信息，请参阅第21.4节。

\subsubsubsection{19.2.4\hspace{0.2cm}通用迭代器的累加}

结束对特性和策略的介绍之前，有必要看看accum()的另一个版本，它增加了处理通用迭代器(而不仅仅是指针)的功能，这是业界通用组件所期望的。这仍然允许用指针调用accum()，因为C++标准库提供了迭代器特性(特征到处都是!)。因此，可以这样定义accum()的初始版本(忽略后面的改进):

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}C++11中，必须将返回类型声明为VT。
\end{tcolorbox}

\hspace*{\fill} \\ %插入空行
\noindent
\textit{traits/accum0.hpp}
\begin{lstlisting}[style=styleCXX]
#ifndef ACCUM_HPP
#define ACCUM_HPP

#include <iterator>

template<typename Iter>
auto accum (Iter start, Iter end)
{
	using VT = typename std::iterator_traits<Iter>::value_type;
	VT total{}; // assume this actually creates a zero value
	while (start != end) {
		total += *start;
		++start;
	}
	return total;
}

#endif // ACCUM_HPP
\end{lstlisting}

std::iterator\_traits结构封装了迭代器的所有相关属性。因为指针的偏特化存在，所以这些特征可以与普通指针类型一起使用。以下是标准库的实现:

\begin{lstlisting}[style=styleCXX]
namespace std {
	template<typename T>
	struct iterator_traits<T*> {
		using difference_type = ptrdiff_t;
		using value_type = T;
		using pointer = T*;
		using reference = T&;
		using iterator_category = random_access_iterator_tag ;
	};
}
\end{lstlisting}

因为没有迭代器所指向值的累积类型，所以需要自行设计AccumulationTraits。









